#!/usr/bin/perl
use strict;
use warnings;

use Config::IniFiles;
use Getopt::Long;
use DBI;
use Time::HiRes qw(gettimeofday);

$SIG{'INT'} = 'IGNORE';

#table name
my $tablename = 'logger';
#sql string to drop table
my $sql_droptable = 'DROP TABLE '.$tablename;
#sql string to create table
my $sql_createtable = 'CREATE TABLE '.$tablename.' (
						   `db_epoch` TIMESTAMP,
						   `servername` VARCHAR(64), 
						   `log_epoch` TIMESTAMP, 
						   `logstring` TEXT)';
#sql script to insert						   
my $sql_insert = 'INSERT INTO '.$tablename.' VALUES(FROM_UNIXTIME(?), ?, FROM_UNIXTIME(?), ?)';
#global DB handler
my $dbh;

#setup defaults
my $configfile	= '';
my $silent        = 0;
my $verbose 	= 0; 
my $foreground	= 0;
my $logfile		= 'log-dumper.log';
my $dump		= 0;
my $createtable	= 0;
my @logfiles	= ();
my $leavefiles	= 0;
my $help		= 0;

my $LOGFILE;

#split line as cvs 
sub csvsplit {
        my $line = shift;
        my $sep = (shift or ';');

        return () unless $line;

        my @cells;
        $line =~ s/\r?\n$//;

        my $re = qr/(?:^|$sep)(?:"([^"]*)"|([^$sep]*))/;

        while($line =~ /$re/g) {
                my $value = defined $1 ? $1 : $2;
                push @cells, (defined $value ? $value : '');
        }

        return @cells;
}

sub out {
	my $date = localtime();
	my $message = $date." ".$_[0]."\n";
	if (!$silent) {
		print $message;
	}
	if (defined $LOGFILE) {
		print $LOGFILE $message;
	}
}

sub fin {
	out "program exited";
	if ($dbh) 
    {
		$dbh->disconnect;
    }
    if (defined $LOGFILE) {
		close $LOGFILE;
	}
	close();
}

#handle die call for correct program finish
local $SIG{__DIE__} = sub {
    my $signal = @_[0];
    out($signal);
	fin();
};

#check for command line arg
GetOptions(
    'configfile=s'   => \$configfile,
    'silent!'    	 => \$silent,
    'logfile=s'		 => \$logfile,
    'dump!'			 => \$dump,
    'createtable!'	 => \$createtable,
    'files=s'		 => sub {
						my($sub_name,$str) = @_;
						my @s = split ',', $str;
						push @logfiles, @s;},
    'leavefiles!'	 => \$leavefiles,
    'help!'  		 => \$help,
);

if($help || $configfile eq '') {
print "Program usage: log-dump.pl -config=configfile - logd config file
\t[-silent\t\t\t]	- no console output
\t[-logfile=logfile\t\t]	- log file name
\t[-dump\t\t\t\t]	- send dump signal to logd
\t[-createtable\t\t\t]	- create table if needed
\t[-files <list of files>\t\t]	- list of files to process
\t[-leavefiles\t\t\t]	- leave files after process
\t[-help\t\t\t\t]	- show this help\n"; die "Program exited";
} 

if($logfile eq '') {
	$logfile = './log-dump.log';
}
print 'using logfile: '.$logfile."\n" ;
open $LOGFILE, ">>$logfile" or die "cannot open logfile $logfile for append: $!";
open STDERR, ">>$logfile";
####stage 1 - read config file
out "reading ".$configfile;
my $cfg = Config::IniFiles->new( -file => $configfile ) || die "Can't read config file: $!";
my $logpath = $cfg->val("logfile", "path");
my $db_host = $cfg->val("db", "host") || die "Can't read config file: $!";
my $db_port = $cfg->val("db", "port") || die "Can't read config file: $!";
my $db_name = $cfg->val("db", "name") || die "Can't read config file: $!";
my $db_user = $cfg->val("db", "user") || die "Can't read config file: $!";
my $db_pass = $cfg->val("db", "pass") || die "Can't read config file: $!";
my $lockfile = $cfg->val("lockfile", "path") || die "Can't read config file: $!";

#file list to process
my @files;
####stage 2 - list and store log dir content
if (!@logfiles) {
	out "using ".$logpath." as log path dir";
	out "store log files list";
	#open log directory
	opendir(my $dir,$logpath) || die "Can't open log dir: $!";
	#read only *.log files
	@files = grep { /\.log$/ } readdir $dir;
	#if no files - exit
	if ($#files+1 eq 0) { die "no *.log files in log folder, exiting" };
} else {
	out "use arg-defined file list";
	@files = @logfiles;
}
if (!@files) {
	die "no log files to process, exit";
}
#print file array
foreach  my $file (@files) {
	out "file ".$file." will be used";
}

#### stage 3 - connect to server

out "connecting to DB $db_host on port $db_port as user $db_user...";
#db connect
$dbh = DBI->connect("DBI:mysql:database=".$db_name.";host=".$db_host.";port=".$db_port, $db_user, $db_pass,
	            { RaiseError => 1 }) || die "Error connecting to database";
out "OK";

#### stage 4 - send SIGUSR2 to daemon for log dump
out "reading lock file $lockfile for daemon pid...";
open (my $lock, "$lockfile") || die "$!";
if (!$lock) {
	die "can't read lock file";
} else { 
	out "OK";
	out "read lock file for daemon PID...";
	my $pid = <$lock>;
	out $pid;
	close ($lock);
	#send SIGUSR2 to pid for dump
	if ($dump) {
		out "send SIGUSR2 to logd";
		kill USR2 => $pid;
	}
}

#drop table
#out "dropping table...";
#my $rc = $dbh->do($sql_droptable) or die $dbh->errstr;
#out "ok";

#create table
if ($createtable) {
	out "creating table...";
	my $rc = $dbh->do($sql_createtable) or die $dbh->errstr;
	out "ok";
}

#$dbh->trace($dbh->parse_trace_flags('SQL|1|test'));

#for each file:
my $totallines = 0;
my $errors = 0;
foreach my $file (@files) {
	out "processing file ".$logpath."/".$file."";
	open (my $fcont, $logpath."/".$file) or out "$!";
	#for each line:
	my $linecount = 0;
	foreach my $line (<$fcont>) {
		#parse cvs line
		my @cells = csvsplit($line);
		#out join("\n",@cells),"";
		#create SQL INSERT
		my $time = gettimeofday();
		if ($dbh->do($sql_insert, undef, $time,@cells)) {}
		else { out $dbh->errstr;$errors++;}
		$linecount++;
	}
	out "$file processed $linecount rows inserted, $errors errors";
	if (!$leavefiles) {
		out "removing $logpath/$file";
		if (unlink $logpath."/".$file) {} 
		else { out "Can't remove $logpath/$file";$errors++;}
	}
	out "All files processes $totallines inserted, $errors errors";
}
fin();
